package adminlte.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * 查看请求调用链，EnableWebSecurity打开debug=true
 * @author ZHUFEIFEI
 */
@Configuration
@EnableWebSecurity(debug = false)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Lazy
    @Autowired
    private SessionRegistry sessionRegistry;

    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 授权base uri,默认值为/oauth2/authorization
     * @see org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer
     */
    private final String AUTHORIZATION_BASE_URI = "/oauth2/authorization";

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(this.userDetailsService);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
                .and()
                .formLogin()
                //登陆页面默认由DefaultLoginGeneratingFilter生成，下面自定义配置，使用自定义登陆页面
                .loginPage("/login").failureUrl("/login?error").permitAll()
                //登陆成功跳转url
                .defaultSuccessUrl("/index")
                .and()
                .logout().logoutRequestMatcher(new OrRequestMatcher(
                    new AntPathRequestMatcher("/logout", HttpMethod.POST.name()),
                    new AntPathRequestMatcher("/logout", HttpMethod.GET.name())
                ))
                .and()
                .oauth2Login().loginPage("/login").failureUrl("/login?error").permitAll()
                .defaultSuccessUrl("/index")
                .authorizationEndpoint().baseUri(AUTHORIZATION_BASE_URI)
                .and()
                .redirectionEndpoint().baseUri("/login/oauth2/callback/*");

        http.headers().cacheControl()
                .and().frameOptions().sameOrigin();

        http.sessionManagement()
                .maximumSessions(1)
                .sessionRegistry(this.sessionRegistry)
                .and()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);

        http.csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository());

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public HttpSessionEventPublisher sessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    /**
     * login页面的第三方登陆信息
     * @see OAuth2LoginConfigurer#getLoginLinks()
     * @param clientRegistrationRepository
     * @return loginUrl-ClientName
     */
    @Bean(name = "loginUrlToClientName")
    public Map<String, String> initOAuth2Client(ClientRegistrationRepository clientRegistrationRepository) {
        Iterable<ClientRegistration> clientRegistrations = null;
        ResolvableType type = ResolvableType.forInstance(clientRegistrationRepository).as(Iterable.class);
        if (type != ResolvableType.NONE && ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
            clientRegistrations = (Iterable<ClientRegistration>) clientRegistrationRepository;
        }
        if (clientRegistrations == null) {
            return Collections.emptyMap();
        }

        String authorizationRequestBaseUri = this.AUTHORIZATION_BASE_URI;
        Map<String, String> loginUrlToClientName = new HashMap<>();
        clientRegistrations.forEach(registration -> loginUrlToClientName.put(
                authorizationRequestBaseUri + "/" + registration.getRegistrationId(),
                registration.getClientName()));
        return loginUrlToClientName;
    }
}
